﻿using System;
using CalcSharp.Core;
using CalcSharp.Core.Containers;

namespace plug_mtrximage.HelperClasses
{
    public enum BlendMode //Algorithms from the Gimp! user manual
    {
        Addition,
        Burn,
        DarkenOnly,
        Difference,
        Divide,
        Dodge,
        GrainExtract,
        GrainMerge,
        HardLight,
        LightenOnly,
        Multiply,
        Overlay,
        Screen,
        SoftLight,
        Subtract,
    }

    public partial class Efects
    {
        private Efects() { }

        public static AMatrix MergeImages(AMatrix m1, AMatrix m2, BlendMode mode, MtrxStorageType t)
        {
            if (m1.Rows * m1.Columns != m2.Rows * m2.Columns) throw new CSException("Input image size mismatch");
            int i, j, Rs;
            Int96 rcol = new Int96();
            Int24 icol1 = new Int24();
            Int24 icol2 = new Int24();
            AMatrix ret;
            
            switch (t)
            {
                case MtrxStorageType.tint24:
                    ret = new Int24Matrix(m1.Rows, m1.Columns);
                    break;
                case MtrxStorageType.tdouble:
                    ret = new DoubleMatrix(m1.Rows, m1.Columns);
                    break;
                case MtrxStorageType.tfloat:
                    ret = new FloatMatrix(m1.Rows, m1.Columns);
                    break;
                default:
                    ret = new FloatMatrix(m1.Rows, m1.Columns);
                    break;
            }

            switch (mode)
            {
                case BlendMode.Addition:
                    #region Addition
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = Math.Min((icol1.R + icol2.R), 255);
                            rcol.G = Math.Min((icol1.G + icol2.G), 255);
                            rcol.B = Math.Min((icol1.B + icol2.B), 255);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Burn:
                    #region Burn
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = (255 - (((255 - icol1.R) * 256) / (icol1.R + icol2.R)));
                            rcol.G = (255 - (((255 - icol1.G) * 256) / (icol1.G + icol2.G)));
                            rcol.B = (255 - (((255 - icol1.B) * 256) / (icol1.B + icol2.B)));
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.DarkenOnly:
                    #region Darken Only
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = Math.Min(icol1.R, icol2.R);
                            rcol.G = Math.Min(icol1.G, icol2.G);
                            rcol.B = Math.Min(icol1.B, icol2.B);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Difference:
                    #region Difference
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = Math.Abs(icol1.R - icol2.R);
                            rcol.G = Math.Abs(icol1.G - icol2.G);
                            rcol.B = Math.Abs(icol1.B - icol2.B);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Divide:
                    #region Divide
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R * 255) / (icol2.R + 1));
                            rcol.G = ((icol1.G * 255) / (icol2.G + 1));
                            rcol.B = ((icol1.B * 255) / (icol2.B + 1));
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Dodge:
                    #region Dodge
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R * 256) / (255 - icol2.R) + 1);
                            rcol.G = ((icol1.G * 256) / (255 - icol2.G) + 1);
                            rcol.B = ((icol1.B * 256) / (255 - icol2.B) + 1);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.GrainExtract:
                    #region Grain Extract
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R - icol2.R) + 128);
                            rcol.G = ((icol1.G - icol2.G) + 128);
                            rcol.B = ((icol1.B - icol2.B) + 128);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.GrainMerge:
                    #region Grain Merge
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R + icol2.R) - 128);
                            rcol.G = ((icol1.G + icol2.G) - 128);
                            rcol.B = ((icol1.B + icol2.B) - 128);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.HardLight:
                    #region Hard Light
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            if (icol1.R > 128) rcol.R = (255 - (((255 - icol1.R) * (255 - (2 * (icol2.R - 128)))) / 256));
                            else rcol.R = ((icol1.R * icol2.R * 2) / 256);
                            if (icol1.G > 128) rcol.G = (255 - (((255 - icol1.G) * (255 - (2 * (icol2.G - 128)))) / 256));
                            else rcol.G = ((icol1.G * icol2.G * 2) / 256);
                            if (icol1.B > 128) rcol.B = (255 - (((255 - icol1.B) * (255 - (2 * (icol2.B - 128)))) / 256));
                            else rcol.B = ((icol1.B * icol2.B * 2) / 256);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.LightenOnly:
                    #region Lighten Only
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = Math.Max(icol1.R, icol2.R);
                            rcol.G = Math.Max(icol1.G, icol2.G);
                            rcol.B = Math.Max(icol1.B, icol2.B);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Multiply:
                    #region multiply
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R * icol2.R) / 255);
                            rcol.G = ((icol1.G * icol2.G) / 255);
                            rcol.B = ((icol1.B * icol2.B) / 255);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Overlay:
                    #region Overlay
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = ((icol1.R / 255) * (icol1.R + ((icol2.R * 2) / 255) * (255 - icol1.R)));
                            rcol.G = ((icol1.G / 255) * (icol1.G + ((icol2.G * 2) / 255) * (255 - icol1.G)));
                            rcol.B = ((icol1.B / 255) * (icol1.B + ((icol2.B * 2) / 255) * (255 - icol1.B)));
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Screen:
                    #region Screen
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = (255 - (((255 - icol2.R) * (255 - icol1.R)) / 255));
                            rcol.G = (255 - (((255 - icol2.G) * (255 - icol1.G)) / 255));
                            rcol.B = (255 - (((255 - icol2.B) * (255 - icol1.B)) / 255));
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.SoftLight:
                    #region soft light
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            Rs = (255 - (((255 - icol2.R) * (255 - icol1.R)) / 255));
                            rcol.R = ((((255 - icol1.R) * icol2.R * icol1.R) + (icol1.R * Rs)) / 255);
                            Rs = (255 - (((255 - icol2.G) * (255 - icol1.G)) / 255));
                            rcol.G = ((((255 - icol1.G) * icol2.G * icol1.G) + (icol1.G * Rs)) / 255);
                            Rs = (255 - (((255 - icol2.B) * (255 - icol1.B)) / 255));
                            rcol.B = ((((255 - icol1.B) * icol2.B * icol1.B) + (icol1.B * Rs)) / 255);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
                case BlendMode.Subtract:
                    #region Subtract
                    for (i = 0; i < m1.Rows; i++)
                    {
                        for (j = 0; j < m1.Columns; j++)
                        {
                            icol1.FromInt32((int)m1[i, j]);
                            icol2.FromInt32((int)m2[i, j]);
                            rcol.R = Math.Min((icol1.R - icol2.R), 0);
                            rcol.G = Math.Min((icol1.G - icol2.G), 0);
                            rcol.B = Math.Min((icol1.B - icol2.B), 0);
                            ret[i, j] = rcol.ToInt32();
                        }
                    }
                    break;
                    #endregion
            }
            return ret;
        }
    }
}
